#include "wifi_plugin.h"

// Windows headers
#include <windows.h>
#include <wlanapi.h>
#include <objbase.h>
#pragma comment(lib, "wlanapi.lib")

// Flutter
#include <flutter/method_channel.h>
#include <flutter/standard_method_codec.h>

#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <vector>

using flutter::EncodableList;
using flutter::EncodableMap;
using flutter::EncodableValue;

namespace wifi_plugin {

// ---------- helpers ----------
static std::string SsidToUtf8(const DOT11_SSID& ssid) {
  if (ssid.uSSIDLength == 0) return std::string();
  return std::string(reinterpret_cast<const char*>(ssid.ucSSID),
                     ssid.uSSIDLength);
}

static bool IsOpenNetwork(const WLAN_AVAILABLE_NETWORK& net) {
  return (net.dot11DefaultAuthAlgorithm == DOT11_AUTH_ALGO_80211_OPEN) &&
         (net.dot11DefaultCipherAlgorithm == DOT11_CIPHER_ALGO_NONE);
}

static bool GetPrimaryWlanInterface(HANDLE wlan, GUID& out_guid) {
  PWLAN_INTERFACE_INFO_LIST list = nullptr;
  DWORD ret = WlanEnumInterfaces(wlan, nullptr, &list);
  if (ret != ERROR_SUCCESS || !list || list->dwNumberOfItems == 0) {
    if (list) WlanFreeMemory(list);
    return false;
  }
  // pick first interface
  out_guid = list->InterfaceInfo[0].InterfaceGuid;
  WlanFreeMemory(list);
  return true;
}
// --------------------------------

WifiPlugin::WifiPlugin() {}
WifiPlugin::~WifiPlugin() {}

void WifiPlugin::RegisterWithRegistrar(
    flutter::PluginRegistrarWindows* registrar) {
  auto channel = std::make_unique<flutter::MethodChannel<EncodableValue>>(
      registrar->messenger(), "wifi_plugin",
      &flutter::StandardMethodCodec::GetInstance());

  auto plugin = std::make_unique<WifiPlugin>();

  channel->SetMethodCallHandler(
      [plugin_pointer = plugin.get()](const auto& call, auto result) {
        plugin_pointer->HandleMethodCall(call, std::move(result));
      });

  registrar->AddPlugin(std::move(plugin));
}

void WifiPlugin::HandleMethodCall(
    const flutter::MethodCall<EncodableValue>& call,
    std::unique_ptr<flutter::MethodResult<EncodableValue>> result) {
  const std::string& method = call.method_name();

  if (method == "scanNetworks") {
    HANDLE wlan = nullptr;
    DWORD ver = 2;
    if (WlanOpenHandle(2, nullptr, &ver, &wlan) != ERROR_SUCCESS || !wlan) {
      result->Error("SCAN_FAILED", "WlanOpenHandle failed");
      return;
    }
    GUID guid{};
    if (!GetPrimaryWlanInterface(wlan, guid)) {
      WlanCloseHandle(wlan, nullptr);
      result->Error("NO_IFACE", "No WLAN interface");
      return;
    }

    // trigger a scan, then read the available list
    WlanScan(wlan, &guid, nullptr, nullptr, nullptr);

    PWLAN_AVAILABLE_NETWORK_LIST net_list = nullptr;
    DWORD ret = WlanGetAvailableNetworkList(wlan, &guid, 0, nullptr, &net_list);
    if (ret != ERROR_SUCCESS || !net_list) {
      if (net_list) WlanFreeMemory(net_list);
      WlanCloseHandle(wlan, nullptr);
      result->Error("SCAN_FAILED", "WlanGetAvailableNetworkList failed");
      return;
    }

    // group by SSID, keep strongest
    std::map<std::string, const WLAN_AVAILABLE_NETWORK*> best;
    for (unsigned int i = 0; i < net_list->dwNumberOfItems; ++i) {
      const auto& n = net_list->Network[i];
      std::string ssid = SsidToUtf8(n.dot11Ssid);
      if (ssid.empty()) continue;  // skip hidden
      auto it = best.find(ssid);
      if (it == best.end() || n.wlanSignalQuality > it->second->wlanSignalQuality) {
        best[ssid] = &n;
      }
    }

    EncodableList out;
    out.reserve(best.size());
    for (const auto& kv : best) {
      const auto* n = kv.second;
      bool is_open = IsOpenNetwork(*n);
      int rssi = static_cast<int>(n->wlanSignalQuality) / 2 - 100;  // approx
      EncodableMap m;
      m[EncodableValue("ssid")] = EncodableValue(kv.first);
      m[EncodableValue("isOpen")] = EncodableValue(is_open);
      m[EncodableValue("rssi")] = EncodableValue(rssi);
      out.emplace_back(EncodableValue(m));
    }

    WlanFreeMemory(net_list);
    WlanCloseHandle(wlan, nullptr);
    result->Success(EncodableValue(out));
    return;
  }

  if (method == "connectWithPassword") {
    const auto* args = std::get_if<EncodableMap>(call.arguments());
    if (!args) {
      result->Error("INVALID_ARGS", "need ssid/password");
      return;
    }
    auto it_ssid = args->find(EncodableValue("ssid"));
    if (it_ssid == args->end() || !std::holds_alternative<std::string>(it_ssid->second)) {
      result->Error("INVALID_ARGS", "ssid missing or invalid");
      return;
    }
    const std::string ssid = std::get<std::string>(it_ssid->second);

    std::string pwd;
    auto it_pwd = args->find(EncodableValue("password"));
    if (it_pwd != args->end() && std::holds_alternative<std::string>(it_pwd->second)) {
      pwd = std::get<std::string>(it_pwd->second);
    }

    HANDLE wlan = nullptr;
    DWORD ver = 2;
    if (WlanOpenHandle(2, nullptr, &ver, &wlan) != ERROR_SUCCESS || !wlan) {
      result->Success(EncodableValue(false));
      return;
    }
    GUID guid{};
    if (!GetPrimaryWlanInterface(wlan, guid)) {
      WlanCloseHandle(wlan, nullptr);
      result->Success(EncodableValue(false));
      return;
    }

    // Build WLANProfile XML
    std::ostringstream xml;
    xml << "<?xml version=\"1.0\"?>\n"
           "<WLANProfile xmlns=\"http://www.microsoft.com/networking/WLAN/profile/v1\">\n"
           "  <name>" << ssid << "</name>\n"
           "  <SSIDConfig><SSID><name>" << ssid << "</name></SSID></SSIDConfig>\n"
           "  <connectionType>ESS</connectionType>\n"
           "  <connectionMode>manual</connectionMode>\n"
           "  <MSM><security>\n";
    if (pwd.empty()) {
      xml << "    <authEncryption><authentication>open</authentication><encryption>none</encryption><useOneX>false</useOneX></authEncryption>\n";
    } else {
      // WPA2-PSK AES (common default)
      xml << "    <authEncryption><authentication>WPA2PSK</authentication><encryption>AES</encryption><useOneX>false</useOneX></authEncryption>\n"
             "    <sharedKey><keyType>passPhrase</keyType><protected>false</protected><keyMaterial>" << pwd << "</keyMaterial></sharedKey>\n";
    }
    xml << "  </security></MSM>\n"
           "</WLANProfile>\n";
    const std::string xml_str = xml.str();

    std::wstring wxml(xml_str.begin(), xml_str.end());
    DWORD reason = 0;
    DWORD flags = WLAN_PROFILE_USER;
    DWORD err = WlanSetProfile(wlan, &guid, flags, wxml.c_str(), nullptr, TRUE, nullptr, &reason);
    if (err != ERROR_SUCCESS) {
      WlanCloseHandle(wlan, nullptr);
      result->Success(EncodableValue(false));
      return;
    }

    WLAN_CONNECTION_PARAMETERS params{};
    params.wlanConnectionMode = wlan_connection_mode_profile;
    std::wstring wname(ssid.begin(), ssid.end());
    params.strProfile = wname.c_str();
    params.dot11BssType = dot11_BSS_type_any;
    params.pDot11Ssid = nullptr;
    params.pDesiredBssidList = nullptr;
    params.dwFlags = 0;

    err = WlanConnect(wlan, &guid, &params, nullptr);
    WlanCloseHandle(wlan, nullptr);
    result->Success(EncodableValue(err == ERROR_SUCCESS));
    return;
  }

  result->NotImplemented();
}

}  // namespace wifi_plugin
